/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "vsf.h"

void vsfip_netdrv_on_outputted(struct vsfip_netdrv_t *netdrv, vsf_err_t err)
{
	if (netdrv->txbuf.netbuf != NULL)
	{
		void *txbuf = netdrv->txbuf.netbuf;
		netdrv->txbuf.netbuf = NULL;

		if (netdrv->connected)
		{
			void *netif = netdrv->netif;
			netdrv->adapter->on_outputted(netif, txbuf, err);
		}
		else
		{
			netdrv->adapter->free_buf(txbuf);
		}
	}
}

void vsfip_netdrv_on_inputted(struct vsfip_netdrv_t *netdrv, vsf_err_t err)
{
	if (netdrv->rxbuf.netbuf != NULL)
	{
		void *rxbuf = netdrv->rxbuf.netbuf;
		netdrv->rxbuf.netbuf = NULL;

		if (!err && netdrv->connected)
		{
			void *netif = netdrv->netif;
			uint8_t *ptr = netdrv->rxbuf.buffer.buffer;
			uint32_t len = netdrv->rxbuf.buffer.size;

			if (netdrv->cb->on_data != NULL)
				netdrv->cb->on_data(netdrv->cb->param, ptr, len, false);
			netdrv->adapter->on_inputted(netif, rxbuf, len);
		}
		else
		{
			netdrv->adapter->free_buf(rxbuf);
		}
	}

	if (netdrv->connected && !netdrv->rxbuf.netbuf)
	{
		netdrv->rxbuf.buffer.size = netdrv->header_size + netdrv->mtu;
		netdrv->rxbuf.netbuf = netdrv->adapter->alloc_buf(&netdrv->rxbuf.buffer);
	}
	if (netdrv->connected && netdrv->rxbuf.netbuf != NULL)
		netdrv->op->input(netdrv);
}

struct vsfip_netdrv_t *vsfip_netdrv_new(uint32_t priv_size,
		const struct vsfip_link_op_t *op, struct vsfip_netdrv_cb_t *cb)
{
#ifdef VSFIP_NETDRV_PNP
	struct vsfip_netdrv_t *netdrv = vsf_bufmgr_malloc(sizeof(*netdrv) + priv_size);
	if (netdrv != NULL)
	{
		memset(netdrv, 0, sizeof(*netdrv));
		netdrv->param = (void *)&netdrv[1];
		netdrv->op = op;
		netdrv->cb = cb;
	}
	return netdrv;
#else
	return NULL;
#endif
}

void vsfip_netdrv_free(struct vsfip_netdrv_t *netdrv)
{
#ifdef VSFIP_NETDRV_PNP
	if (netdrv->tofree)
		vsf_bufmgr_free(netdrv);
#endif
}

vsf_err_t vsfip_netdrv_init(struct vsfip_netdrv_t *netdrv)
{
	vsf_err_t err = netdrv->op->init(netdrv);
	if (!err && !netdrv->rxbuf.netbuf)
		vsfip_netdrv_on_inputted(netdrv, VSFERR_FAIL);
	return err;
}

vsf_err_t vsfip_netdrv_fini(struct vsfip_netdrv_t *netdrv)
{
	return netdrv->op->fini(netdrv);
}

uint8_t *vsfip_netdrv_header(struct vsfip_netdrv_t *netdrv, void *netbuf, int32_t len)
{
	return netdrv->adapter->header(netbuf, len);
}

vsf_err_t vsfip_netdrv_connect(struct vsfip_netdrv_t *netdrv)
{
	vsf_err_t err = VSFERR_FAIL;
	netdrv->connected = true;
	if (netdrv->cb->on_connect)
		netdrv->cb->on_connect(netdrv->cb->param, netdrv);
	if (netdrv->adapter != NULL)
	{
		err = netdrv->adapter->on_connect(netdrv->netif);
		if (!err)
		{
			if (netdrv->cb->on_connected)
				netdrv->cb->on_connected(netdrv->cb->param, netdrv);
		}
	}
	return err;
}

void vsfip_netdrv_disconnect(struct vsfip_netdrv_t *netdrv)
{
	netdrv->connected = false;
	if (netdrv->cb->on_disconnect)
		netdrv->cb->on_disconnect(netdrv->cb->param, netdrv);
	netdrv->adapter->on_disconnect(netdrv->netif);
}

bool vsfip_netdrv_connected(struct vsfip_netdrv_t *netdrv)
{
	return netdrv->connected;
}

vsf_err_t vsfip_netdrv_output(struct vsfip_netdrv_t *netdrv, void *netbuf,
		uint8_t *ptr, uint32_t len)
{
	vsf_err_t err = VSFERR_FAIL;
	if (netdrv->connected && !netdrv->txbuf.netbuf)
	{
		netdrv->txbuf.netbuf = netbuf;
		netdrv->txbuf.buffer.buffer = ptr;
		netdrv->txbuf.buffer.size = len;
		if (netdrv->cb->on_data != NULL)
			netdrv->cb->on_data(netdrv->cb->param, ptr, len, true);
		err = netdrv->op->output(netdrv);
	}

	if (err)
		netdrv->adapter->on_outputted(netdrv->netif, netbuf, VSFERR_FAIL);
	return err;
}
